﻿using UnityEngine;
using System.Collections;
using System.Threading;
using System;
using System.IO;
using UnityEditor;
using UnityEngine.Tilemaps;
using System.Collections.Generic;

[Serializable]
public class NeuralNetShader
{
    [NonSerialized]
    public int count =0;
    public string name="iRobot";
    public int saveTime = 5;
    public bool saving = false;
    public int inputNum = 256, inputHeight=256;
	public int outputNum = 256, outputHeight=256;
    public int hideLayNum = 256;
    public int hideWidth = 256,hideHeight=256;
    public int hideLinLen = 24;
	public int hideLinNum = 3;
    public int threadNum = 5;
	public float inputDecRate = 0.3f, powerIncRate=0.03f,powerDecRate = 0.003f;
    public float layerIncRate = 0.01f, layerDecRate = 0.01f;
    public bool seatchLink = false;
    public bool initing=false;

    public float[][][] layer;
    public short[][][][] linkX;
    public short[][][][] linkY;
    public short[][][][] linkZ;
    public float[][][][] linkPower;

    [NonSerialized]
    public float[][][] input;
    [NonSerialized]
    public bool runing;

    public delegate void EventHandlerUpdate();

    [NonSerialized]
    public EventHandlerUpdate run;
    public int sleep=8, updateSpeed=125;//8=120帧/秒；  16=60帧每秒
    private System.Random random = new System.Random();

    private static string persistentDataPath = ".";

    public static int loadData = 0;

    public struct NeuralLink
    {
        public float input;
        public float layer;
        public float power;
    }

    public struct NeuralData
    {
        public int x;
        public int y;
        public int z;
        public float input;
        public float layer;
        public NeuralLink link1;
        public NeuralLink link2;
        public NeuralLink link3;
    }
    public void Init(){
        initing = true;
		layer = new float[hideLayNum + 2][][];
        linkX = new short[hideLayNum + 1][][][];
        linkY = new short[hideLayNum + 1][][][];
        linkZ = new short[hideLayNum + 1][][][];
        linkPower = new float[hideLayNum + 1][][][];

		// input layer
		layer[0] = new float[inputNum][];
        linkX[0] = new short[inputNum][][];
        linkY[0] = new short[inputNum][][];
        linkZ[0] = new short[inputNum][][];
        linkPower[0] = new float[inputNum][][];
		for(int i=0;i<inputNum;i++){
            layer[0][i] = new float[inputHeight];
            linkX[0][i] = new short[inputHeight][];
            linkY[0][i] = new short[inputHeight][];
            linkZ[0][i] = new short[inputHeight][];
            linkPower[0][i] = new float[inputHeight][];
            for (int j = 0; j < inputHeight; j++)
            {
                layer[0][i][j] = 0;
                linkX[0][i][j] = new short[hideLinNum];
                linkY[0][i][j] = new short[hideLinNum];
                linkZ[0][i][j] = new short[hideLinNum];
                linkPower[0][i][j] = new float[hideLinNum];
                for (int k = 0; k < hideLinNum; k++)
                {
                    int si = (int)Math.Round(1f*hideWidth*i/inputNum);
                    int sj = (int)Math.Round(1f*hideHeight*j/inputHeight);
                    linkX[0][i][j][k] = 1;
                    linkY[0][i][j][k] = (short)random.Next(Math.Max(0,si-hideLinLen), Math.Min(si + hideLinLen+1, hideWidth));
                    linkZ[0][i][j][k] = (short)random.Next(Math.Max(0, sj - hideLinLen), Math.Min(sj + hideLinLen + 1, hideHeight));
                    linkPower[0][i][j][k] = random.Next(-9999, 10000) / 10000f;
                }
            }
		}
		// hide layer
        hideLinNum = Mathf.Min(8, hideLinNum);
		for(int i=1;i<=hideLayNum;i++){
            count = i;
			layer[i] = new float[hideWidth][];
            linkX[i] = new short[hideWidth][][];
            linkY[i] = new short[hideWidth][][];
            linkZ[i] = new short[hideWidth][][];
            linkPower[i] = new float[hideWidth][][];
			for(int j=0;j<hideWidth;j++){
                layer[i][j] = new float[hideHeight];
                linkX[i][j] = new short[hideHeight][];
                linkY[i][j] = new short[hideHeight][];
                linkZ[i][j] = new short[hideHeight][];
                linkPower[i][j] = new float[hideHeight][];
                for (int z = 0; z < hideHeight; z++)
                {
                    layer[i][j][z] = 0;
                    linkX[i][j][z] = new short[hideLinNum];
                    linkY[i][j][z] = new short[hideLinNum];
                    linkZ[i][j][z] = new short[hideLinNum];
                    linkPower[i][j][z] = new float[hideLinNum];
                    for (int k = 0; k < hideLinNum; k++)
                    {
                        linkPower[i][j][z][k] = random.Next(-9999, 10000) / 10000f; ;
                        //linkX[i][j][k] = i < hideLayNum ? UnityEngine.Random.Range(1, hideLayNum + 1) : hideLayNum + 1;
                        //linkY[i][j][k] = UnityEngine.Random.Range(-1, (i < hideLayNum?hideWidth:outputNum));
                        if (i == hideLayNum)
                        {
                            int sj = (int)Math.Round(1f * j / hideWidth * outputNum);
                            int sz = (int)Math.Round(1f * z / hideHeight * outputHeight);
                            linkX[i][j][z][k] = (short)(hideLayNum + 1);
                            linkY[i][j][z][k] = (short)random.Next(Math.Max(0, sj - hideLinLen), Math.Min(sj + hideLinLen + 1, outputNum));
                            linkZ[i][j][z][k] = (short)random.Next(Math.Max(0, sz - hideLinLen), Math.Min(sz + hideLinLen + 1, outputHeight));
                            //Debug.Log("HideLayEndLayLink:" +j+"@"+ k + "  ->  " + linkY[i][j][k]);
                        }
                        else
                        {
                            linkX[i][j][z][k] = (short)random.Next(Mathf.Max(1, i - hideLinLen), Mathf.Min(i + hideLinLen + 1, hideLayNum + 1));
                            linkY[i][j][z][k] = (short)random.Next(Mathf.Max(0, j - hideLinLen), Mathf.Min(j + hideLinLen + 1, layer[i].Length));
                            linkZ[i][j][z][k] = (short)random.Next(Math.Max(0, z - hideLinLen), Math.Min(z + hideLinLen + 1, hideHeight));
                        }

                    }
                }
			}
		}
       
		// output layer
		layer[hideLayNum+1] = new float[outputNum][];
        for (int i = 0; i < outputNum; i++)
        {
            layer[hideLayNum + 1][i] = new float[outputHeight];
        }
        
        //Debug.Log("NN Init End");
        initing = false;
	}

    public void InitInput()
    {
        input = new float[hideLayNum + 2][][];
        input[0] = new float[inputNum][];
        for (int i = 0; i < inputNum; i++)
        {
            input[0][i] = new float[inputHeight];
        }
        for (int i = 1; i <= hideLayNum; i++)
        {
            input[i]= new float[hideWidth][];
            for (int j = 0; j < hideWidth; j++)
            {
                input[i][j] = new float[hideHeight];
            }
        }
        input[hideLayNum + 1] = new float[outputNum][];
        for (int i = 0; i < outputNum; i++)
        {
            input[hideLayNum + 1][i] = new float[outputHeight];
        }
    }

	public int inwidth(){
		return layer [0].Length;
	}

	public int outwidth(){
		return layer [hideLayNum + 1].Length;
	}

    public void inputValue(int i,int z, float val)
    {
        if (i >= 0 && i < input[0].Length && z >= 0 && z < input[0][i].Length)
        {
            input[0][i][z] += val;
        }
        //if (short.IsInfinity(input[0][i]))
        //{
        //    input[0][i] = short.MaxValue;
        //}
    }
    public void inputValue(int i,int j,int z, float val)
    {
        input[i][j][z] += val;
        //if (float.IsInfinity(input[i][j]))
        //{
        //    input[i][j] = float.MaxValue;
        //}
    }
    public float getOutInputLayer(int j, int z)
    {
        return (input[hideLayNum + 1][j][z] + layer[hideLayNum + 1][j][z]);
    }
    public float getInputLayer(int i, int j, int z)
    {
        return (input[i][j][z] + layer[i][j][z]);
    }

    public bool checkOut(int i,int z){
        if (input != null && input[hideLayNum + 1] != null && layer != null && layer[hideLayNum + 1] != null)
        {
            if (i < outputNum&&z<outputHeight)
            {
                return input[hideLayNum + 1][i][z] + layer[hideLayNum + 1][i][z] > 0;
            }
        }
        return false;
	}
    public bool check(int i, int j, int z)
    {
        if (input != null && input[i] != null && layer != null && layer[i] != null)
        {
            if (i < input.Length && j < input[i].Length)
            {
                return input[i][j][z] + layer[i][j][z] > 0;
            }
        }
        return false;
    }

    //public void clear(int i)
    //{
    //    if (input != null)
    //    {
    //        input[hideLayNum + 1][i] = 0;
    //    }
    //}

	// Use this for initialization
	public virtual void Start () {
        runing = true;

        for (short i = 0; i < threadNum; i++)
        {
            short tid = i;
            Debug.Log("ThreadUpdateStart:" + tid);

            Thread t = new Thread(() =>
            {
                Thread.Sleep(10000);
                for (long n = 0; runing; n++)
                {
                    ThreadUpdate(tid, n);
                    Thread.Sleep(sleep);
                }
                Debug.Log("ThreadUpdateEnd:" + tid);
            });
            t.IsBackground = true;
            t.Start();  //其实用的也是ThreadStart
        }
        //Thread rthread = new Thread(() =>
        //{
        //    Debug.Log("RootThreadUpdateStart");
        //    while (runing)
        //    {
        //        ThreadUpdate();
        //        Thread.Sleep(1);
        //    }
        //    Debug.Log("RootThreadUpdateEnd");
        //});
        //rthread.Start();  //其实用的也是ThreadStart
        neuralData = new List<NeuralData[]>();
        for (int i = 0; i < threadNum; i++)
        {
            neuralData.Add(new NeuralData[hideWidth * hideHeight]);
        }
    }

    private List<NeuralData[]> neuralData;
    private static object lockObj = new System.Object();
    void ThreadUpdate(short tid,long count)
    {
        short n = (short)(hideLayNum / threadNum), v = 0, tv = 0, k = 0, destX = 0, destY = 0, destZ = 0;
        int tvn = 0, currIdx=0;
        bool hascomp = false;
        if (tid == 0)
        {
            // input layer
            for (int j = 0; j < inputNum; j++)
            {
                for (int z = 0; z < inputHeight; z++)
                {
                    for (k = 0; k < hideLinNum; k++)
                    {
                        destY = linkY[0][j][z][k];
                        destZ = linkZ[0][j][z][k];
                        if (destY < 0 || destZ < 0)
                        {
                            //搜索可连接节点
                            int si = (int)Math.Round(1f * hideWidth * j / inputNum);
                            int sj = (int)Math.Round(1f * hideHeight * z / inputHeight);
                            if (seatchLink)
                            {
                                for (int w = Math.Max(0, si - hideLinLen); destY == -1 && w < Math.Min(si + hideLinLen + 1, hideWidth); w++)
                                {
                                    for (int h = Math.Max(0, sj - hideLinLen); h < Math.Min(sj + hideLinLen + 1, hideHeight); h++)
                                    {
                                        if (input[1][w][h] + layer[1][w][h] > 0)
                                        {
                                            linkX[0][j][z][k] = 1;
                                            linkY[0][j][z][k] = destY = (short)w;
                                            linkZ[0][j][z][k] = destZ = (short)h;
                                            linkPower[0][j][z][k] = random.Next(-9999, 10000) / 10000f;// * (layer[0][i][j] > layer[1][offsetY][offsetZ] ? 1f : -1f);
                                            break;
                                        }
                                    }
                                    if (destY > 0)
                                    {
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                linkY[0][j][z][k] = destY = (short)random.Next(Math.Max(0, si - hideLinLen), Math.Min(si + hideLinLen + 1, hideWidth));
                                linkZ[0][j][z][k] = destZ = (short)random.Next(Math.Max(0, sj - hideLinLen), Math.Min(sj + hideLinLen + 1, hideHeight));
                                if (input[1][destY][destZ] + layer[1][destY][destZ] > 0)
                                {
                                    linkPower[0][j][z][k] = random.Next(-9999, 10000) / 10000f;// * (layer[0][i][j] > layer[1][offsetY][offsetZ] ? 1f : -1f);
                                }
                                else
                                {
                                    linkY[0][j][z][k] = destY = -1;
                                }
                            }
                            if (destY < 0 || destZ < 0)
                            {
                                continue;
                            }
                        }
                    }
                    if (input[0][j][z] != 0)
                    {
                        writeNerualData(tid, currIdx++, 0, j, z);
                        hascomp = true;
                    }
                }
            }
            if (hascomp)
            {
                SoundNeuralNetToShader.Compute c = SoundNeuralNetToShader.EnqueueCompute(this, neuralData[tid], currIdx);
                while (c.DataGet == null)
                {
                    Thread.Sleep(sleep);
                }
                neuralData[tid] = c.DataGet;
                readNeuralData(tid, currIdx);
                hascomp = false;
                currIdx = 0;
            }
        }

        //Debug.Log("ThreadUpdate:" + ti);

        // hide layer  0111/111/111/111
        for (short i = (short)(1 + (n * tid)); i <= ((tid + ((tid + 1 == threadNum) ? 2 : 1)) * n) && i <= hideLayNum; i++)
        {
            //Debug.Log("ThreadUpdate:"+ti+"-" + i);
            for (short j = 0; j < hideWidth; j++)
            {
                for (int z = 0; z < hideHeight; z++)
                {
                        //Debug.Log("NNUpdateHideLay: "+i+","+j+": "+v);
                        for (k = 0; k < hideLinNum; k++)
                        {
                            destX = linkX[i][j][z][k];
                            destY = linkY[i][j][z][k];
                            destZ = linkZ[i][j][z][k];
                            if (destX < 0 || destY<0 ||destZ<0)
                            {
                                if (i > 0 && i < hideLayNum)
                                {
                                    if (seatchLink)
                                    {
                                        //雷达式搜索可连接节点
                                        for (short l = 1; l <= hideLinLen && destX == -1; l++)
                                        {
                                            // 圈右
                                            if (i + l <= hideLayNum)
                                            {
                                                for (short m = (short)-l; m < l && destX == -1; m++)
                                                {
                                                    short x = (short)(i + l), y = (short)(j + m);
                                                    if (y < 0) continue;
                                                    if (y >= layer[x].Length) break;
                                                    for (int sz = Math.Max(0, z - l); sz <= Math.Min(hideHeight - 1, z + l); sz++)
                                                    {
                                                        if (input[x][y][sz] + layer[x][y][sz] > 0)
                                                        {
                                                            linkX[i][j][z][k] = destX = x;
                                                            linkY[i][j][z][k] = destY = y;
                                                            linkZ[i][j][z][k] = destZ = (short)sz;
                                                            linkPower[i][j][z][k] *= 100;// powerRate * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                            //Debug.Log("NNUpdateHideLay: search link ok! "+i+","+j+"=>"+l+","+m);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                            //圈底
                                            if (j + l < hideWidth)
                                            {
                                                for (short m = l; m > -l && destX == -1; m--)
                                                {
                                                    short x = (short)(i + m), y = (short)(j + l);
                                                    if (x <= 0) continue;
                                                    if (x > hideLayNum || y >= layer[x].Length) break;
                                                    for (int sz = Math.Max(0, z - l); sz <= Math.Min(hideHeight - 1, z + l); sz++)
                                                    {
                                                        if (input[x][y][sz] + layer[x][y][sz] > 0)
                                                        {
                                                            linkX[i][j][z][k] = destX = x;
                                                            linkY[i][j][z][k] = destY = y;
                                                            linkZ[i][j][z][k] = destZ = (short)sz;
                                                            linkPower[i][j][z][k] *= 100;// powerRate * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                            //Debug.Log("NNUpdateHideLay: search link ok! "+i+","+j+"=>"+l+","+m);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                            //圈左
                                            if (i - l > 0)
                                            {
                                                for (short m = l; m > -l && destX == -1; m--)
                                                {
                                                    short x = (short)(i - l), y = (short)(j + m);
                                                    if (y < 0) continue;
                                                    if (y >= layer[x].Length) break;
                                                    for (int sz = Math.Max(0, z - l); sz <= Math.Min(hideHeight - 1, z + l); sz++)
                                                    {
                                                        if (input[x][y][sz] + layer[x][y][sz] > 0)
                                                        {
                                                            linkX[i][j][z][k] = destX = x;
                                                            linkY[i][j][z][k] = destY = y;
                                                            linkZ[i][j][z][k] = destZ = (short)sz;
                                                            linkPower[i][j][z][k] *= 100;// powerRate * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                            //Debug.Log("NNUpdateHideLay: search link ok! "+i+","+j+"=>"+l+","+m);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                            // 圈顶
                                            if (j - l >= 0)
                                            {
                                                for (short m = (short)-l; m < l && destX == -1; m++)
                                                {
                                                    short x = (short)(i + m), y = (short)(j - l);
                                                    if (x <= 0) continue;
                                                    if (x > hideLayNum || y >= layer[x].Length) break;
                                                    for (int sz = Math.Max(0, z - l); sz <= Math.Min(hideHeight - 1, z + l); sz++)
                                                    {
                                                        if (input[x][y][sz] + layer[x][y][sz] > 0)
                                                        {
                                                            linkX[i][j][z][k] = destX = x;
                                                            linkY[i][j][z][k] = destY = y;
                                                            linkZ[i][j][z][k] = destZ = (short)sz;
                                                            linkPower[i][j][z][k] *= 100;// powerRate * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                            //Debug.Log("NNUpdateHideLay: search link ok! "+i+","+j+"=>"+l+","+m);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                            //顶层
                                            if (z + l < hideHeight)
                                            {
                                                for (short m = (short)(-l + 1); m < l && destX == -1; m++)
                                                {
                                                    short x = (short)(i + m), sz = (short)(z + l);
                                                    if (x <= 0) continue;
                                                    if (x > hideLayNum) break;
                                                    for (int y = Math.Max(0, j - l + 1); y <= Math.Min(hideWidth - 1, j + l - 1); y++)
                                                    {
                                                        if (input[x][y][sz] + layer[x][y][sz] > 0)
                                                        {
                                                            linkX[i][j][z][k] = destX = x;
                                                            linkY[i][j][z][k] = destY = (short)y;
                                                            linkZ[i][j][z][k] = destZ = (short)sz;
                                                            linkPower[i][j][z][k] *= 100;// powerRate * (layer[i][j][z] > layer[offsetX][offsetY][offsetZ] ? 1f : -1f);
                                                            //Debug.Log("NNUpdateHideLay: search link ok! "+i+","+j+"=>"+l+","+m);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                            //底层
                                            if (z - l >= 0)
                                            {
                                                for (short m = (short)(-l + 1); m < l && destX == -1; m++)
                                                {
                                                    short x = (short)(i + m), sz = (short)(z - l);
                                                    if (x <= 0) continue;
                                                    if (x > hideLayNum) break;
                                                    for (int y = Math.Max(0, j - l + 1); y <= Math.Min(hideWidth - 1, j + l - 1); y++)
                                                    {
                                                        if (input[x][y][sz] + layer[x][y][sz] > 0)
                                                        {
                                                            linkX[i][j][z][k] = destX = x;
                                                            linkY[i][j][z][k] = destY = (short)y;
                                                            linkZ[i][j][z][k] = destZ = (short)sz;
                                                            linkPower[i][j][z][k] *= 100;// powerRate * (layer[i][j][z] > layer[offsetX][offsetY][offsetZ] ? 1f : -1f);
                                                            //Debug.Log("NNUpdateHideLay: search link ok! "+i+","+j+"=>"+l+","+m);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        linkX[i][j][z][k] = destX = (short)random.Next(Mathf.Max(1, i - hideLinLen), Mathf.Min(i + hideLinLen + 1, hideLayNum + 1));
                                        linkY[i][j][z][k] = destY = (short)random.Next(Mathf.Max(0, j - hideLinLen), Mathf.Min(j + hideLinLen + 1, layer[i].Length));
                                        linkZ[i][j][z][k] = destZ = (short)random.Next(Math.Max(0, z - hideLinLen), Math.Min(z + hideLinLen + 1, hideHeight));
                                        if (input[destX][destY][destZ] + layer[destX][destY][destZ] > 0)
                                        {
                                            linkPower[i][j][z][k] = random.Next(-9999, 10000) / 10000f;// * (layer[i][j][z] > layer[offsetX][offsetY][offsetZ] ? 1f : -1f);
                                        }
                                        else
                                        {
                                            linkX[i][j][z][k] = destX = -1;
                                        }
                                    }
                                }
                                else
                                {
                                    int sj = (int)Math.Round((1f * j / hideWidth) * outputNum);
                                    int sz = (int)Math.Round((1f * z / hideHeight) * outputHeight);
                                    if (seatchLink)
                                    {
                                        for (int m = Math.Max(0, sj - hideLinLen); destX < 0 && m < Math.Min(sj + hideLinLen + 1, outputNum); m++)
                                        {
                                            short x = (short)(hideLayNum + 1), y = (short)m;
                                            for (int h = Math.Max(0, sz - hideLinLen); h < Math.Min(sz + hideLinLen + 1, outputHeight); h++)
                                            {
                                                if (input[x][y][h] + layer[x][y][h] > 0)
                                                {
                                                    linkX[i][j][z][k] = destX = x;
                                                    linkY[i][j][z][k] = destY = y;
                                                    linkZ[i][j][z][k] = destZ = (short)h;
                                                   
                                                    linkPower[i][j][z][k] = random.Next(-9999, 10000) / 10000f;// * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                    break;
                                                   
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        linkX[i][j][z][k] = destX = (short)(hideLayNum + 1);
                                        linkY[i][j][z][k] = destY = (short)random.Next(Math.Max(0, sj - hideLinLen), Math.Min(sj + hideLinLen + 1, outputNum));
                                        linkZ[i][j][z][k] = destZ = (short)random.Next(Math.Max(0, sz - hideLinLen), Math.Min(sz + hideLinLen + 1, outputHeight));
                                        if(input[destX][destY][destZ] + layer[destX][destY][destZ] > 0)
                                        {
                                            linkPower[i][j][z][k] = random.Next(-9999, 10000) / 10000f;// * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                        }
                                        else
                                        {
                                            linkX[i][j][z][k] = destX = -1;
                                        }
                                        
                                    }
                                }
                                if (destX < 0|| destY < 0|| destZ < 0)
                                {
                                    continue;//搜索完后处理下一个链接
                                }
                            }
                        }

                    if (input[i][j][z] != 0)
                    {
                        writeNerualData(tid, currIdx++, i, j, z);
                        hascomp = true;
                    }
                }
            }
            if (hascomp)
            {
                SoundNeuralNetToShader.Compute c = SoundNeuralNetToShader.EnqueueCompute(this, neuralData[tid],currIdx);
                while (c.DataGet == null)
                {
                    Thread.Sleep(sleep);
                }
                neuralData[tid] = c.DataGet;
                readNeuralData(tid, currIdx);
                hascomp = false;
                currIdx = 0;
            }
        }

        // output layer
        if (tid == threadNum - 1)
        {
            try
            {
                run();
                for (int j = 0; j < outputNum; j++)
                {
                    for (int z = 0; z < outputHeight; z++)
                    {
                        if (input[hideLayNum + 1][j][z] != 0)
                        {
                            writeNerualData(tid,currIdx++, hideLayNum+1, j, z);
                            hascomp = true;
                        }
                    }
                }
                if (hascomp)
                {
                    SoundNeuralNetToShader.Compute c = SoundNeuralNetToShader.EnqueueCompute(this, neuralData[tid],currIdx);
                    while (c.DataGet == null)
                    {
                        Thread.Sleep(sleep);
                    }
                    neuralData[tid] = c.DataGet;
                    readNeuralData(tid,currIdx);
                    hascomp = false;
                    currIdx = 0;
                }
            }
            catch (Exception ex)
            {
                Debug.LogError(ex.ToString());
            }
        }
    }

    private void writeNerualData(int tid,int currIdx, int i, int j, int z)
    {
        
        NeuralData[] neuralDatas = neuralData[tid];
        NeuralData data = neuralDatas[currIdx];
        //if (i == 0)
        //{
        //    data = neuralDatas[j * inputHeight + z];
        //}
        //else if (i <= hideLayNum)
        //{
        //    data = neuralDatas[j * hideHeight + z];
        //}
        //else
        //{
        //    data = neuralDatas[j * outputHeight + z];
        //}
        data.x = i;
        data.y = j;
        data.z = z;
        data.input = input[i][j][z];
        data.layer = layer[i][j][z];

        //if (data.input!=0)
        //{
        //    Debug.Log(name + " writeNerualData  " + data.x+","+data.y+","+data.z+"  "+data.input+"/"+data.layer);
        //}

        if (i <= hideLayNum)
        {
            //data.link1 = new NeuralLink();
            int lx = linkX[i][j][z][0];
            int ly = linkY[i][j][z][0];
            int lz = linkZ[i][j][z][0];
            if (lx >= 0 && ly >= 0 && lz >= 0)
            {
                data.link1.input = 0;
                data.link1.power = linkPower[i][j][z][0];
                data.link1.layer = layer[lx][ly][lz];
            }
            else
            {
                data.link1.input = 0;
                data.link1.layer = 0;
                data.link1.power = 0;
            }

            //data.link2 = new NeuralLink();
            int lx2 = linkX[i][j][z][1];
            int ly2 = linkY[i][j][z][1];
            int lz2 = linkZ[i][j][z][1];
            if (lx2 >= 0 && ly2 >= 0 && lz2 >= 0)
            {
                data.link2.input = 0;
                data.link2.power = linkPower[i][j][z][1];
                data.link2.layer = layer[lx2][ly2][lz2];
            }
            else
            {
                data.link2.input = 0;
                data.link2.layer = 0;
                data.link2.power = 0;
            }

            //data.link3 = new NeuralLink();
            int lx3 = linkX[i][j][z][2];
            int ly3 = linkY[i][j][z][2];
            int lz3 = linkZ[i][j][z][2];
            if (lx3 >= 0 && ly3 >= 0 && lz3 >= 0)
            {
                data.link3.input = 0;
                data.link3.power = linkPower[i][j][z][2];
                data.link3.layer = layer[lx3][ly3][lz3];
            }
            else
            {
                data.link3.input = 0;
                data.link3.layer = 0;
                data.link3.power = 0;
            }
        }
        neuralDatas[currIdx] = data;
        //if (i == 0)
        //{
        //    neuralDatas[j * inputHeight + z] = data;
        //}
        //else if (i <= hideLayNum)
        //{
        //    neuralDatas[j * hideHeight + z] = data;
        //}
        //else
        //{
        //    neuralDatas[j * outputHeight + z] = data;
        //}
    }

    private void readNeuralData(int tid, int count)
    {
        for (int i = 0; i < count; i++)
        {
            NeuralData data = neuralData[tid][i];
            input[data.x][data.y][data.z] = data.input;
            layer[data.x][data.y][data.z] = data.layer;

            //if (data.x>= hideLayNum && data.layer != 0)
            //{
            //    Debug.Log(name+" neural " + data.x + "," + data.y + ", " + data.z + "  " + data.input + "/" + data.layer);
            //}
            if (data.x<=hideLayNum)
            {
                int lX = linkX[data.x][data.y][data.z][0];
                int lY = linkY[data.x][data.y][data.z][0];
                int lZ = linkZ[data.x][data.y][data.z][0];
                if (lX >= 0 && lY >= 0 && lZ >= 0)
                {
                    input[lX][lY][lZ] += data.link1.input;
                    linkPower[data.x][data.y][data.z][0] = data.link1.power;
                    //if (i % 1000 == 0&& data.link1.input!= 0)
                    //{
                    //    Debug.Log(data.x + "," + data.y + "," + data.z + " -0- (" + linkX + "," + linkY + "," + linkZ + ")  " + data.link1.input + "/"+ data.link1.layer + "/" + data.link1.power);
                    //}
                    if (data.link1.power == 0) { linkX[data.x][data.y][data.z][0] = -1; }
                }

                int lX2 = linkX[data.x][data.y][data.z][1];
                int lY2 = linkY[data.x][data.y][data.z][1];
                int lZ2 = linkZ[data.x][data.y][data.z][1];
                if (lX2 >= 0 && lY2 >= 0 && lZ2 >= 0)
                {
                    input[lX2][lY2][lZ2] += data.link2.input;
                    linkPower[data.x][data.y][data.z][1] = data.link2.power;
                    //if (i % 1000 == 0 && data.link2.input != 0)
                    //{
                    //    Debug.Log(data.x + "," + data.y + "," + data.z + " -1- (" + linkX2 + "," + linkY2 + "," + linkZ2 + ")  " + data.link2.input + "/" + data.link2.layer + "/" + data.link2.power);
                    //}
                    if (data.link2.power == 0) { linkX[data.x][data.y][data.z][1] = -1; }
                }

                int lX3 = linkX[data.x][data.y][data.z][2];
                int lY3 = linkY[data.x][data.y][data.z][2];
                int lZ3 = linkZ[data.x][data.y][data.z][2];
                if (lX3 >= 0 && lY3 >= 0 && lZ3 >= 0)
                {
                    input[lX3][lY3][lZ3] += data.link3.input;
                    linkPower[data.x][data.y][data.z][2] = data.link3.power;
                    //if (i % 1000 == 0 && data.link3.input != 0)
                    //{
                    //    Debug.Log(data.x + "," + data.y + "," + data.z + " -2- (" + linkX3 + "," + linkY3 + "," + linkZ3 + ")  " + data.link3.input + "/" + data.link3.layer + "/" + data.link3.power);
                    //}
                    if (data.link3.power == 0) { linkX[data.x][data.y][data.z][2] = -1; }
                }
            }
        }
    }

    // Update is called once per frame
    public virtual void ThreadUpdate()
    {
        //Debug.Log("RootThreadUpdate");

        //if (float.IsInfinity(input[hideLayNum + 1][i]))
        //{
        //    input[hideLayNum + 1][i] = 0;
        //}
        //if (float.IsNegativeInfinity(layer[hideLayNum + 1][i]))
        //{
        //    layer[hideLayNum + 1][i] = 0;
        //}
        //if (float.IsNaN(layer[hideLayNum + 1][i]))
        //{
        //    layer[hideLayNum + 1][i] = 0;
        //}


    }

    public void OnDestroy()
    {
        runing = false;
    }
    public void OnApplicationQuit(){
        input = null;
        layer = null;
        linkX = null;
        linkY = null;
        linkZ = null;
        linkPower = null;
        Debug.Log("NN: OnApplicationQuit");
    }

    internal void OnSerialize(BinaryWriter bfs)
    {
        bfs.Write((short)inputNum);
        bfs.Write((short)inputHeight);
        bfs.Write((short)outputNum);
        bfs.Write((short)outputHeight);
        bfs.Write((short)hideLayNum);
        bfs.Write((short)hideWidth);
        bfs.Write((short)hideHeight);
        bfs.Write((short)hideLinLen);
        bfs.Write((short)hideLinNum);
        bfs.Write((short)threadNum);
        bfs.Write(inputDecRate);
        bfs.Write(powerIncRate);
        bfs.Write(powerDecRate);
        bfs.Write(layerIncRate); 
        bfs.Write(layerDecRate);
        bfs.Write((short)sleep);
        bfs.Write((short)updateSpeed);
        int length = layer.Length;
        bfs.Write((short)length);
        for (int i = 0; i < length; i++)
        {
            float[][] arr = layer[i];
            bfs.Write((short)arr.Length);
            bfs.Write((short)arr[0].Length);
            for (int j = 0; j < arr.Length; j++)
            {
                for (int z = 0; z < arr[j].Length; z++)
                {
                    bfs.Write(arr[j][z]);
                    if (i < length - 1)
                    {
                        for (int k = 0; k < hideLinNum; k++)
                        {
                            bfs.Write(linkX[i][j][z][k]);
                            bfs.Write(linkY[i][j][z][k]);
                            bfs.Write(linkZ[i][j][z][k]);
                            bfs.Write(linkPower[i][j][z][k]);
                        }
                    }
                }
            }
        }
        
    }
    
    internal void OnUnSerialize(BinaryReader bfs)
    {
        inputNum = bfs.ReadInt16();
        inputHeight = bfs.ReadInt16();
        outputNum = bfs.ReadInt16();
        outputHeight = bfs.ReadInt16();
        hideLayNum = bfs.ReadInt16();
        hideWidth = bfs.ReadInt16();
        hideHeight = bfs.ReadInt16();
        hideLinLen = bfs.ReadInt16();
        hideLinNum = bfs.ReadInt16();
        threadNum = bfs.ReadInt16();
        inputDecRate = bfs.ReadSingle();
        powerIncRate = bfs.ReadSingle();
        powerDecRate = bfs.ReadSingle();
        layerIncRate = bfs.ReadSingle();
        layerDecRate = bfs.ReadSingle();
        sleep = bfs.ReadInt16();
        updateSpeed = bfs.ReadInt16();
        int length = bfs.ReadInt16();
        layer = new float[length][][];
        linkX = new short[length][][][];
        linkY = new short[length][][][];
        linkZ = new short[length][][][];
        linkPower = new float[length][][][];
        for (int i = 0; i < length; i++)
        {
            count = i;
            int w = bfs.ReadInt16();
            int h = bfs.ReadInt16();
            float[][] arr = new float[w][];
            layer[i] = arr;
            linkX[i] = new short[w][][];
            linkY[i] = new short[w][][];
            linkZ[i] = new short[w][][];
            linkPower[i] = new float[w][][];
            for (int j = 0; j < w; j++)
            {
                arr[j] = new float[h];
                linkX[i][j] = new short[h][];
                linkY[i][j] = new short[h][];
                linkZ[i][j] = new short[h][];
                linkPower[i][j] = new float[h][];
                for (int z = 0; z < h; z++)
                {
                    arr[j][z] = bfs.ReadSingle();
                    if (i < length - 1)
                    {
                        linkX[i][j][z] = new short[hideLinNum];
                        linkY[i][j][z] = new short[hideLinNum];
                        linkZ[i][j][z] = new short[hideLinNum];
                        linkPower[i][j][z] = new float[hideLinNum];
                        for (int k = 0; k < hideLinNum; k++)
                        {
                            linkX[i][j][z][k] = bfs.ReadInt16();
                            linkY[i][j][z][k] = bfs.ReadInt16();
                            linkZ[i][j][z][k] = bfs.ReadInt16();
                            linkPower[i][j][z][k] = bfs.ReadSingle();
                        }
                    }
                }
            }
        }
    }

    internal void StartLoad()
    {
        persistentDataPath = Application.persistentDataPath;

        loadData++;

        Thread rthread = new Thread(() =>
        {
            Debug.Log("LoadDataStart");

            LoadData();

            while (loadData != 0)
            {
                Thread.Sleep(1000);
            }
            Start();

            Debug.Log("LoadDataEnd");


            // 定时存档
            while (runing)
            {
                Thread.Sleep(saveTime * 60000);
                Debug.Log("SaveDataStart");
                SaveData();
                Debug.Log("SaveDataEnd");
            }
        });
        rthread.IsBackground = true;
        rthread.Start();
    }

    private bool LoadData()
    {
        bool suc = false;
        if (suc=BinaryDeserialize(name + ".dat"))
        {
            if (layer == null)
            {
                Debug.Log("Load " + name + " ReInit");
                Init();
            }
            //if (nn.inputNum != inputNum || nn.outputNum != outputNum || nn.hideLayNum != hideLayNum || nn.hideWidth != hideWidth || nn.hideLinNum != hideLinNum)
            //{
            //    neuralNet = nn;
            //    Debug.Log("Load " + name + " Data version error");
            //    //Init();
            //}
            //else
            //{
            //    Debug.Log("Load " + name + " success");
            //    neuralNet = nn;
            //}
        }
        else
        {
            Init();
        }


        InitInput();

        lock (this)
        {
            loadData--;
        }
        return suc;
    }
    public void SaveData()
    {
        if (saving) return;

        saving = true;

        bool suc = BinarySerialize(name + ".dat");
        Debug.Log("Save " + name + ".dat " + suc);

        saving = false;
    }

    /// <summary>
    /// 序列化二进制
    /// </summary>
    public bool BinarySerialize(string outputPath)
    {
        //if (!File.Exists(outputPath))
        //{
        //    Debug.LogError($"序列化二进制失败，输出路径有误，filePath：{outputPath}");
        //    return false;
        //}
        try
        {
            String tmpPath = persistentDataPath + "/" + outputPath + ".tmp";
            Debug.Log("Save path: " + tmpPath);
            string dirPath = Path.GetDirectoryName(tmpPath);
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
            using (FileStream fs = new FileStream(tmpPath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
            {
                //BinaryFormatter binaryFormatter = new BinaryFormatter();
                //binaryFormatter.Serialize(fs, obj);
                BufferedStream bfs = new BufferedStream(fs);
                OnSerialize(new BinaryWriter(bfs));
                bfs.Flush();
                bfs.Close();
            }
            File.Delete(persistentDataPath + "/" + outputPath);
            File.Move(tmpPath, persistentDataPath + "/" + outputPath);

        }
        catch (Exception e)
        {
            Debug.LogError("序列化二进制失败，" + e);
            return false;
        }
        return true;
    }
    /// <summary>
    /// 反序列化二进制
    /// </summary>
    public bool BinaryDeserialize(string binaryFilePath)
    {
        binaryFilePath = persistentDataPath + "/" + binaryFilePath;
        Debug.Log("Load Path: " + binaryFilePath);
        if (!File.Exists(binaryFilePath))
        {
            Debug.LogWarning("反序列化二进制失败，找不到二进制文件：" + binaryFilePath);
            return false;
        }
        try
        {
            using (FileStream fs = File.OpenRead(binaryFilePath))
            {
                //BinaryFormatter binaryFormatter = new BinaryFormatter();
                //obj = (T)binaryFormatter.Deserialize(fs);
                BufferedStream bfs = new BufferedStream(fs);
                OnUnSerialize(new BinaryReader(bfs));
                bfs.Close();
            }
        }
        catch (Exception e)
        {
            Debug.LogError("反序列化二进制失败，" + e);
            return false;
        }
        return true;
    }
}
